

#include "main.h"
#include "ikcp.h"


/******************************************************************************/

typedef struct kcp_tunnel_t {
	void (*recv_cb)(void *tunnel, const void *buf, int len);
	void *recv_arg;

	pthread_t ktid;
	pthread_mutex_t kmux;

	void *kcp;
	void *itun;
}kcp_tunnel;


#define KCP_CONV 0x12345678


/******************************************************************************/


int kcp_send(void *tunnel, const void *buf, int len)
{
	kcp_tunnel *ktun = (kcp_tunnel*)tunnel;
	int retv;

	pthread_mutex_lock(&ktun->kmux);
	retv = ikcp_send(ktun->kcp, buf, len);
	pthread_mutex_unlock(&ktun->kmux);

	return retv;
}


void kcp_input(void *tunnel, const void *buf, int len)
{
	kcp_tunnel *ktun = (kcp_tunnel*)tunnel;
	char rbuf[1536];

	pthread_mutex_lock(&ktun->kmux);
	ikcp_input(ktun->kcp, buf, len);
	pthread_mutex_unlock(&ktun->kmux);

	while(1){
		pthread_mutex_lock(&ktun->kmux);
		len = ikcp_recv(ktun->kcp, rbuf, sizeof(rbuf));
		pthread_mutex_unlock(&ktun->kmux);
		if(len<0){
			break;
		}

		ktun->recv_cb(ktun, rbuf, len);
	}

}


/******************************************************************************/


static int kcp_output(const char *buf, int len, void *kcp, void *user)
{
	kcp_tunnel *ktun = (kcp_tunnel*)user;

	return rawio_send(ktun->itun, buf, len);
}

static void *kcp_update_thread(void *arg)
{
	kcp_tunnel *ktun = (kcp_tunnel*)arg;
	int current, next, len;
	char rbuf[1536];

	while(1){
		pthread_testcancel();

		pthread_mutex_lock(&ktun->kmux);

		while(1){
			len = ikcp_recv(ktun->kcp, rbuf, sizeof(rbuf));
			if(len<0){
				break;
			}
			ktun->recv_cb(ktun, rbuf, len);
		}

		current = get_ticks();
		ikcp_update(ktun->kcp, current);
		next = ikcp_check(ktun->kcp, current);
		pthread_mutex_unlock(&ktun->kmux);

		next -= current;
		if(next>0){
			usleep(next*1000);
		}
	}

	return NULL;
}


/******************************************************************************/


void *new_kcp_tunnel(void *itun, void *recv_cb, void *arg)
{
	kcp_tunnel *ktun;

	ktun = (kcp_tunnel*)malloc(sizeof(kcp_tunnel));
	memset(ktun, 0, sizeof(kcp_tunnel));

	ktun->itun = itun;
	ktun->recv_cb = recv_cb;
	ktun->recv_arg = arg;

	ktun->kcp = ikcp_create(KCP_CONV, ktun);

	ikcp_setoutput(ktun->kcp, kcp_output);
	ikcp_setmtu(ktun->kcp, gcfg.mtu+8);    // data + stream_header

	// normal: 0 40 0 0
	//         0 30 2 1
	//  fast : 0 20 2 1
	//  fast2: 1 20 2 1
	//  fast3: 1 10 2 1
	ikcp_nodelay(ktun->kcp, 1, 20, 2, 1);

	ikcp_wndsize(ktun->kcp, 2048, 2048);

	// TODO: set minrto

	pthread_mutex_init(&ktun->kmux, NULL);
	pthread_create(&ktun->ktid, NULL, kcp_update_thread, ktun);

	return ktun;
}


void close_kcp_tunnel(void *tunnel)
{
	kcp_tunnel *ktun = (kcp_tunnel*)tunnel;

	ikcp_release(ktun->kcp);

	pthread_cancel(ktun->ktid);
	pthread_join(ktun->ktid, NULL);

	free(ktun);
}

/******************************************************************************/

